# model

# merges the file-parts for raid-5 together...

from google.appengine.ext import webapp

from Utilities import *

from google.appengine.ext.blobstore import BlobInfo, blobstore
from google.appengine.ext.blobstore.blobstore import BlobReader
from google.appengine.ext.blobstore.blobstore import *
from controller.BuildStorageInfo import BuildStorageInfo


class MergeFile(webapp.RequestHandler):
    def get(self):
        utilities = Utilities()
        
        # get some necessary attributes
        keyname = self.request.get('keyname')
        md5File = self.request.get('md5hash')
        
        username = utilities.getUsername()
        userText = utilities.getUserText()
        
        # routine if user is logged-in or not
        if username:
            urlText = utilities.getUrlTextLogout()
            url = utilities.getLogout(self)
            
            # get raid-level
            raidLevel = utilities.getRaidLevel(username)
            
            if raidLevel == utilities.getRaidFiveText():
                # get the raid-5-info entry (by the md5-value from the file)
                raidFiveEntry = utilities.getRaidFiveInfoEntryByMD5(username, md5File)
                raidFiveResults = raidFiveEntry.fetch(100)
                
                # initialize the md5-values
                md5PartOne = None
                md5PartTwo = None
                md5Parity = None
                
                for raidResult in raidFiveResults:
                    md5PartOne = raidResult.md5PartOne
                    md5PartTwo = raidResult.md5PartTwo
                    md5Parity = raidResult.md5Parity
                
                if md5PartOne != None and md5PartTwo != None and md5Parity != None:
                    self.determineFileParts(utilities, username, userText, urlText, url, raidFiveResults, md5File, md5PartOne, md5PartTwo, md5Parity, keyname)
                else:
#                    hintText = _("The file is broken, so the system couldn't determine all necessary information - please try to upload the file again.")
                    hintText = "The file is broken, so that the system couldn't determine all necessary information - please try to upload the file again."
                    hintColor = 'red'
                    
                    keyList = []
                    self.callBuilder(userText, username, url, urlText, hintText, hintColor, keyList)
        else:
            self.redirect('/')
            
    # determine the necessary file-parts for the complete file
    def determineFileParts(self, utilities, username, userText, urlText, url, raidFiveResults, md5File, md5PartOne, md5PartTwo, md5Parity, keyname):
        # private attribute for a default acl-setting
        __defaultACL = 'public-read'
        bucketname = utilities.getBucketname(username)
        
        amazon = utilities.getAmazonText()
        google = utilities.getGoogleText()
        openStack = utilities.getOpenStackText()
        
        # get the available credentials
        credentials = utilities.getCredentials(username)
        credentialAmount = credentials.count()
        credentialResults = credentials.fetch(100)
        
        divisor = credentialAmount -1
        
        # initialize the storage-connections
        amazonStorageConnection = None
        googleStorageConnection = None
        openStackStorageConnection = None
        
        # initialize key-lists
        amazonKeyList = None
        googleKeyList = None
        openStackKeyList = None
        
        # get the region-names to set up the storage-connections..
        for credentialResult in credentialResults:
            regionname = credentialResult.regionname
            
            if regionname == amazon:
                amazonStorageConnection = utilities.getStorageServiceConnection(username, regionname)
                amazonStorageConnection = amazonStorageConnection[0]
                amazonKeyList = utilities.getBucketKeyList(amazonStorageConnection, bucketname)
            
            if regionname == google:
                googleStorageConnection = utilities.getStorageServiceConnection(username, regionname)
                googleStorageConnection = googleStorageConnection[0]
                googleKeyList = utilities.getBucketKeyList(googleStorageConnection, bucketname)
                
            if regionname == openStack:
                openStackStorageConnection = utilities.getStorageServiceConnection(username, regionname)
                openStackStorageConnection = openStackStorageConnection[0]
                openStackKeyList = utilities.getBucketKeyList(openStackStorageConnection, bucketname)
                
        # initialize some attributes
        storageOne = None
        storageTwo = None
        storageThree = None
        
        # put the key-information in the right order together...
        newFile = ''
        
        contentType = 'application/octet-stream'
        
        # get some further information
        for raidFiveResult in raidFiveResults:
            storageOne = raidFiveResult.storageOne
            storageTwo = raidFiveResult.storageTwo
            storageThree = raidFiveResult.storageThree

        # initialize the string-attributes
        amazonString = ''
        googleString = ''
        openStackString = ''
            
        # get the parts from the key-lists...
        # check for storage-one..
        if storageOne == amazon:
            amazonKey = utilities.getBucketKeyByChecksum(username, amazonKeyList, md5PartOne)
            
            if amazonKey != None:
                amazonKeySize = amazonKey.size
                amazonString = amazonKey.read(amazonKeySize)
                newFile = newFile + amazonString
        elif storageOne == google:
            googleKey = utilities.getBucketKeyByChecksum(username, googleKeyList, md5PartOne)
            
            if googleKey != None:
                googleKeySize = googleKey.size
                googleString = googleKey.read(googleKeySize)
                newFile = newFile + googleString
        elif storageOne == openStack:
            openStackKey = self.getBucketKeyByMD5Checksum(utilities, md5PartOne, openStackKeyList)
            
            if openStackKey != None:
                openStackKeySize = openStackKey.size
                openStackString = openStackKey.read(openStackKeySize)
                newFile = newFile + openStackString
        
        # check for storage-two
        if storageTwo == amazon:
            amazonKey = utilities.getBucketKeyByChecksum(username, amazonKeyList, md5PartTwo)
            
            if amazonKey != None:
                amazonKeySize = amazonKey.size
                amazonString = amazonKey.read(amazonKeySize)
                newFile = newFile + amazonString
        elif storageTwo == google:
            googleKey = utilities.getBucketKeyByChecksum(username, googleKeyList, md5PartTwo)
            
            if googleKey != None:
                googleKeySize = googleKey.size
                googleString = googleKey.read(googleKeySize)
                newFile = newFile + googleString
        elif storageTwo == openStack:
            openStackKey = self.getBucketKeyByMD5Checksum(utilities, md5PartTwo, openStackKeyList)

            if openStackKey != None:
                openStackKeySize = openStackKey.size
                openStackString = openStackKey.read(openStackKeySize)
                newFile = newFile + openStackString
        
        # check for storage-three
        # in the third storage-service is always stored the parity-information. this part is only necessary, when one of the other parts is missing...
        if storageThree == amazon:
            amazonKey = utilities.getBucketKeyByChecksum(username, amazonKeyList, md5Parity)
            
            if amazonKey != None:
                amazonKeySize = amazonKey.size
                amazonString = amazonKey.read(amazonKeySize)
        elif storageThree == google:
            googleKey = utilities.getBucketKeyByChecksum(username, googleKeyList, md5Parity)
            
            if googleKey != None:
                googleKeySize = googleKey.size
                googleString = googleKey.read(googleKeySize)
        elif storageThree == openStack:
            openStackKey = self.getBucketKeyByMD5Checksum(utilities, md5Parity, openStackKeyList)
            
            if openStackKey != None:
                openStackKeySize = openStackKey.size
                openStackString = openStackKey.read(openStackKeySize)

        # check if more than one storage-entries are missing. if it is so, the file couldn't be recover so print a hint-message
        if (amazonKey == None and googleKey == None) or (amazonKey == None and openStackKey == None) or (googleKey == None and openStackKey == None):
#                hintText = _("An error occurred - more than one file-part is broken. File couldn't recover.")
            hintText = "An error occurred - more than one file-part is broken or missing. File couldn't be recover."
            hintColor = 'red'
            
            keyList = amazonKeyList
            if amazonKeyList == None or amazonKeyList == []:
                keyList = googleKey
                if googleKeyList == None or googleKeyList == []:
                    keyList = openStackKeyList
                    if openStackKeyList == None or openStackKeyList == []:
                        keyList = [] 
            
            self.callBuilder(userText, username, url, urlText, hintText, hintColor, keyList)
        
        # at this point we know, that only one file-part is missing, so determine which one, recover and upload it
        if amazonKey == None and googleKey != None and openStackKey != None:
            # recover the part, which should be in the amazon storage and upload it again
            amazonString = self.recoverFilePart(utilities, googleString, openStackString)
            amazonStringLength = len(amazonString)
            
            # determine the blob-key-value
            blobKey = utilities.getBlobInfoKeyFromLocalDatabase(username, keyname)
            
            # convert fromm string to blob-reader-object
            amazonBlobReader = blobstore.BlobReader(blobKey, buffer_size=amazonStringLength, position=0)
            amazonBlobReader.seek(0)
            amazonBlobReader.read(amazonStringLength)
            
            # convert from blob-reader-object to  blob-info-object
            amazonBlobInfo = amazonBlobReader.blob_info
            
            # determine some necessary information
            keyname = amazonBlobInfo.filename
            contentType = amazonBlobInfo.content_type
            lastMod = amazonBlobInfo.creation
            acl = __defaultACL
            keysize = amazonBlobInfo.size/divisor
            
            # create the new key and compute the md5-checksum of the file-part
            nKey = utilities.createNewKey(amazonStorageConnection, bucketname, keyname)
            amazonKeyMD5 = utilities.computeMD5FromString(nKey, amazonString)
            
            # upload the file again in the proposed storage-service
            utilities.uploadFileRaidFive(utilities.getAmazonText(), amazonStorageConnection, bucketname, amazonString, keyname, contentType, lastMod, acl, keysize, amazonKeyMD5)

            # call this method again, to load the file..
            self.determineFileParts(utilities, username, userText, urlText, url, raidFiveResults, md5File, md5PartOne, md5PartTwo, md5Parity, keyname)

        if googleKey == None and amazonKey != None and openStackKey != None:
            # recover the part, which should be in the google storage and upload it again
            googleString = self.recoverFilePart(utilities, amazonString, openStackString)
            googleStringLength = len(googleString)
            
            # determine the blob-key-value
            blobKey = utilities.getBlobInfoKeyFromLocalDatabase(username, keyname)
            
            # convert fromm string to blob-reader-object
            googleBlobReader = blobstore.BlobReader(blobKey, buffer_size=googleStringLength, position=0)
            googleBlobReader.seek(0)
            googleBlobReader.read(googleStringLength)
            
            # convert from blob-reader-object to  blob-info-object
            googleBlobInfo = googleBlobReader.blob_info
            
            # determine some necessary information
            keyname = googleBlobInfo.filename
            contentType = googleBlobInfo.content_type
            lastMod = googleBlobInfo.creation
            acl = __defaultACL
            keysize = googleBlobInfo.size/divisor
            
            # create the new key and compute the md5-checksum of the file-part
            nKey = utilities.createNewKey(googleStorageConnection, bucketname, keyname)
            googleKeyMD5 = utilities.computeMD5FromString(nKey, googleString)
            
            # upload the file again in the proposed storage-service
            utilities.uploadFileRaidFive(utilities.getGoogleText(), googleStorageConnection, bucketname, googleString, keyname, contentType, lastMod, acl, keysize, googleKeyMD5)
            
            # call this method again, to load the file..
            self.determineFileParts(utilities, username, userText, urlText, url, raidFiveResults, md5File, md5PartOne, md5PartTwo, md5Parity, keyname)

        if openStackKey == None and amazonKey != None and googleKey != None:
            # recover the part, which should be in the openstack storage and upload it again
            openStackString = self.recoverFilePart(utilities, googleString, amazonString)
            openStackStringLength = len(openStackString)
            
            # determine the blob-key-value
            blobKey = utilities.getBlobInfoKeyFromLocalDatabase(username, keyname)
            
            # convert fromm string to blob-reader-object
            openStackBlobReader = blobstore.BlobReader(blobKey, buffer_size=openStackStringLength, position=0)
            openStackBlobReader.seek(0)
            openStackBlobReader.read(openStackStringLength)
            
            # convert from blob-reader-object to  blob-info-object
            openStackBlobInfo = openStackBlobReader.blob_info
            
            # determine some necessary information
            keyname = openStackBlobInfo.filename
            contentType = openStackBlobInfo.content_type
            lastMod = openStackBlobInfo.creation
            acl = __defaultACL
            keysize = openStackBlobInfo.size/divisor
            
            # create the new key and compute the md5-checksum of the file-part
            nKey = utilities.createNewKey(openStackStorageConnection, bucketname, keyname)
            openStackKeyMD5 = utilities.computeMD5FromString(nKey, openStackString)
            
            # upload the file again in the proposed storage-service
            utilities.uploadFileRaidFive(utilities.getOpenStackText(), openStackStorageConnection, bucketname, openStackString, keyname, contentType, lastMod, acl, keysize, openStackKeyMD5)
            
            # call this method again, to load the file..
            self.determineFileParts(utilities, username, userText, urlText, url, raidFiveResults, md5File, md5PartOne, md5PartTwo, md5Parity, keyname)
            
        # show the merged file respectively open the download-view of the browser
#        if newFile != '' and amazonKey != None and googleKey != None and openStackKey != None:
        if newFile != None and amazonKey != None and googleKey != None and openStackKey != None:
            # NOTE:
            #    the merged file is contained in the variable 'newFile' and in the local development-environment it works fine to perform the 'show/download' file-process with
            #    the print-method, but in the real GAE-environment this cause an unlikely effect. The file is shown as it's string-representation on the screen, instead of the
            #    real file!
            newFile = newFile + "\n"
            print(newFile)
            
        else:
            hintText = "An error occurred - couldn't get all key-information."
            hintColor = "red"
            
            keyList = []
            self.callBuilder(userText, username, url, urlText, hintText, hintColor, keyList)
        
    # determine a bucket-key by it's checksum (only for openstack!!)
    # there is another method like this in the utilities-class for the other storage-services..
    def getBucketKeyByMD5Checksum(self, utilities, md5, openStackKeyList):
            # get entry from local database (OctopusCloudDatabaseOpenStack)
            localEntry = utilities.getKeyByMD5ForOpenstack(md5)
            
            # fetch the result
            localResults = localEntry.fetch(100)
            
            # initialize the attributes
            keyname = None
            keysize = None
            
            # get the values for the attributes
            for localResult in localResults:
                keyname = localResult.filename
                keysize = localResult.fileSize
            
            openStackKey = utilities.getBucketKeyFromKeyListForOpenStack(keyname, keysize, openStackKeyList)
            return openStackKey
    
    # recover the missing file-part
    def recoverFilePart(self, utilities, stringOne, stringTwo):
        stringOneInt = utilities.convertStringToInt(stringOne)
        stringTwoInt = utilities.convertStringToInt(stringTwo)
#        missingFilePartString = self.computeMissingFilePart(utilities, stringOneInt, stringTwoInt)
        missingFilePartInt = self.computeMissingFilePart(utilities, stringOneInt, stringTwoInt)
        missingFilePartString = utilities.convertIntToString(missingFilePartInt)
        return missingFilePartString
    
    # compute the missing file-part (xor of the other file-parts in their int-representation)
    def computeMissingFilePart(self, utilities, stringOneInt, stringTwoInt):
        if len(stringOneInt) < len(stringTwoInt):
            utilities.adjustStringFileLength(stringOneInt, stringTwoInt)
        
        if len(stringTwoInt) < len(stringOneInt):
            utilities.adjustStringFileLength(stringTwoInt, stringOneInt)
        
        missingFilePart = utilities.performXorOperation(stringOneInt, stringTwoInt)
        return missingFilePart
     
    # responsible for the call of the get-method of the BuildStorageInfo-Class
    def callBuilder(self, userText, username, url, urlText, hintText, hintColor, keyList, isSync=''):
        templateValues = {
                          'userText': userText,
                          'user': username,
                          'urlText': urlText,
                          'url': url,
                          'urlText': urlText,
                          'hintText': hintText,
                          'hintColor': hintColor
                          }
        
        builder = BuildStorageInfo()
        builder.get(self, templateValues, keyList, isSync)
        
    
        